5

先从老朋友 CSS 讲起

我们熟悉的 CSS 风格颜色表示方式,大体有下面几种,canvas 大体是直接沿用这些写法的,但最后包含透明度的写法有些许不同。

  • #RRGGBB:十六进制格式,红绿蓝分别用两位十六进制数表示。
  • #RGB:简写的十六进制格式,转换成 6 位格式时会重复三原色,例如#fb0->#ffbb00
  • rgb(R,G,B):函数表达式,三原色分别由 0~255 的整数值表示。
  • rgba(R,G,B,A):包含透明度的函数表达式,其中 alpha 参数为 0~1,需要指定透明度的颜色必须使用该格式。

作为前端人员平时用得很多,但你可能会一脸懵逼之前自己写的颜色字符串居然是十六进制?
待我细细道来。这里的 R 即是红色(red),G 即是绿色(green),B 即是蓝色(blue),这三个是显示器普遍使用的三基色,属于叠加型原色,百科摘录如下。

【科普】原色是指不能透过其他颜色的混合调配而得出的“基本色”。
以不同比例将原色混合,可以产生出其他的新颜色。以数学的向量空间来解释色彩系统,则原色在空间内可作为一组基底向量,并且能组合出一个“色彩空间”。由于人类肉眼有三种不同颜色的感光体,因此所见的色彩空间通常可以由三种基本色所表达,这三种颜色被称为“三原色”。一般来说叠加型的三原色是红色、绿色、蓝色(又称三基色,用于电视机、投影仪等显示设备);而消减型的三原色是品红色、黄色、青色(用于书本、杂志等的印刷)。

解密颜色值

每一个颜色都是由三基色叠加合成,所以我们需要告诉计算机这各个基色的比例(浓度),将这个比例量化就是一个 0~255 的整数,也可说是 256 个级别,越大即表示各种原色更多(更浓)。

【PS】至于为什么是 256 个级别?
是因为计算机中每个原色用 8 位二进制(0 或 1)表示,也就是 2 的 8 次方共 256。

每个颜色都是 256 个级别,那它的组合的可能就有256*256*256=16777216,换句话说,一个颜色用 24 位二进制表示,换算成十进制就是 0~16777215。
这里你应该可以看懂上面 CSS 颜色表示方式前三个的含义了吧,至于rgba(R,G,B,A)多加入了 A,表示透明度,这个是扩展版的 32 位颜色系统,多了一个额外的 8 位二进制表示透明度的级别,CSS 将它简化成 0~1 表示。


举个例子吧!
#FF55F3这个颜色为例进行讲解。(0x 开头表示十六进制数,js 中不区分大小写,至于不知道什么是十六进制的,请自行百度)
红色是0xFF,绿色是0x55,蓝色0xF3
转换成十进制:红色是 255,绿色是 85,蓝色是 243。也就是说这个数值和rgb(255,85,243)写法是等价的。

【PS】简便的转换方法,直接在控制台打印即可,比如console.log(0xF3);,js 默认输出十进制表示的字符串。

颜色合成

颜色理论学得差不多了,现在来看看合成,已知三原色的值,要如何用代码合成一个颜色呢?
以上面说的#FF55F3为例,现在已知的是各个颜色值,下面提供两种做法:

得到rgb(R,G,B)格式

直接利用 js 数字转换为字符串时默认是十进制的特性。

let r = 0xFF;
let g = 0x55;
let b = 0xF3;
let color = `rgb(${r},${g},${b})`;

得到#RRGGBB格式

一个 24 位的颜色值,二进制即:RRRRRRRRGGGGGGGGBBBBBBBB
红色值左移 16 位,绿色左移 8 位,将三者做“或”就能得到合成的 24 位颜色值,再转成 16 进制字符串即可。

0xFF << 16 = 111111110000000000000000
0x55 << 08 = 000000000101010100000000
0xF3       = 000000000000000011110011
OR         = 111111110101010111110011
//省略跟前面一样的...
let color = `#${(r << 16 | g << 8 | b).toString(16)}`;

颜色分解

合成学完了,现在考虑一下如何用代码分解颜色,也就是把一个颜色分离出红、绿、蓝。
rgb(R,G,B)格式就说了,切字符串就能得到。
重点讨论#RRGGBB格式,其实就是第二种合成方法的逆过程,右移后“与“操作,简单来说就是把想要的颜色值所在的位置移动到末尾,再用“与”0xFF剔除其他颜色。
还是以#FF55F3为例,现已知这个字符串,要求分解出三基色的值。

  1. 切除“#”号得到 16 进制字符串;
  2. 红色:右移 16 位,与 0xFF 做“与”操作;
  3. 绿色:右移 8 位,与 0xFF 做“与”操作;
  4. 蓝色:直接与 0xFF 做“与”操作。
let color = parseInt('#FF55F3'.slice(1), 16);
let r = color >> 16 & 0xFF
let g = color >> 8 & 0xFF
let b = color & 0xFF

以绿色提取过程为例:

0xFF55F3      = 111111110101010111110011
0xFF55F3 >> 8 = 000000001111111101010101
0xFF          = 000000000000000011111111
AND           = 000000000000000001010101

封装颜色工具

当然,上面的合成、分解代码都是基本理论的应用,实际项目中使用会为了健壮性封装成更加合理的工具,可以参考我们工具类 utils.js 中的 colorToRGB() 和 parseColor() 两个函数。

  • colorToRGB() 用于将#RRGGBB格式或任意数字,转换成rgb(R,G,B)rgba(R,G,B,A)
  • parseColor() 用于将#RRGGBB格式转成数字,将数字转成#RRGGBB格式。

calimanco
1.4k 声望766 粉丝

老朽对真理的追求从北爱尔兰到契丹无人不知无人不晓。